"""
OKX交易所适配器

支持现货和合约交易
"""

import time
import hmac
import hashlib
import base64
import json
from datetime import datetime, timezone
from typing import Dict, List, Optional, Any
import requests
import pandas as pd

from .base_exchange import BaseExchange
from ..core import unified_logger


class OKXExchange(BaseExchange):
    """OKX交易所适配器"""
    
    def __init__(self, config: dict):
        super().__init__(config)
        self.name = 'okx'
        
        # API配置
        self.api_key = config.get('api_key', '')
        self.secret_key = config.get('secret_key', '')
        self.passphrase = config.get('passphrase', '')
        self.sandbox = config.get('sandbox', True)
        
        # API端点
        if self.sandbox:
            self.base_url = 'https://www.okx.com'  # 测试环境
        else:
            self.base_url = 'https://www.okx.com'  # 生产环境
        
        self.logger = unified_logger.get_logger(f"okx_exchange")
        
        # 费率配置
        self.spot_maker_fee = config.get('fees', {}).get('spot', {}).get('maker', 0.0008)
        self.spot_taker_fee = config.get('fees', {}).get('spot', {}).get('taker', 0.001)
        self.futures_maker_fee = config.get('fees', {}).get('futures', {}).get('maker', 0.0002)
        self.futures_taker_fee = config.get('fees', {}).get('futures', {}).get('taker', 0.0005)
    
    def _generate_signature(self, timestamp: str, method: str, 
                           request_path: str, body: str = '') -> str:
        """生成签名"""
        message = timestamp + method + request_path + body
        mac = hmac.new(
            bytes(self.secret_key, encoding='utf8'),
            bytes(message, encoding='utf-8'),
            digestmod=hashlib.sha256
        )
        return base64.b64encode(mac.digest()).decode()
    
    def _get_headers(self, method: str, request_path: str, body: str = '') -> Dict[str, str]:
        """获取请求头"""
        timestamp = datetime.now(timezone.utc).isoformat()[:-6] + 'Z'
        
        headers = {
            'OK-ACCESS-KEY': self.api_key,
            'OK-ACCESS-SIGN': self._generate_signature(timestamp, method, request_path, body),
            'OK-ACCESS-TIMESTAMP': timestamp,
            'OK-ACCESS-PASSPHRASE': self.passphrase,
            'Content-Type': 'application/json'
        }
        
        if self.sandbox:
            headers['x-simulated-trading'] = '1'
        
        return headers
    
    def _make_request(self, method: str, endpoint: str, 
                     params: Optional[Dict] = None, 
                     data: Optional[Dict] = None) -> Dict:
        """发送API请求"""
        url = f"{self.base_url}{endpoint}"
        
        # 准备请求体
        body = ''
        if data:
            body = json.dumps(data)
        
        # 准备请求头
        headers = self._get_headers(method, endpoint, body)
        
        try:
            if method == 'GET':
                response = requests.get(url, headers=headers, params=params, timeout=10)
            elif method == 'POST':
                response = requests.post(url, headers=headers, data=body, timeout=10)
            else:
                raise ValueError(f"不支持的HTTP方法: {method}")
            
            response.raise_for_status()
            result = response.json()
            
            if result.get('code') != '0':
                raise Exception(f"API错误: {result.get('msg', '未知错误')}")
            
            return result
            
        except Exception as e:
            self.logger.error(f"API请求失败: {e}")
            raise
    
    def connect(self) -> bool:
        """连接到交易所"""
        try:
            # 测试连接
            self.get_spot_balance()
            self.connected = True
            self.logger.info("OKX交易所连接成功")
            return True
        except Exception as e:
            self.logger.error(f"连接OKX交易所失败: {e}")
            self.connected = False
            return False
    
    def disconnect(self):
        """断开连接"""
        self.connected = False
        self.logger.info("已断开OKX交易所连接")
    
    # 实现抽象方法
    def get_spot_balance(self) -> Dict[str, Any]:
        """获取现货账户余额"""
        try:
            result = self._make_request('GET', '/api/v5/account/balance')
            balances = {}
            
            if result.get('data'):
                for account in result['data']:
                    for detail in account.get('details', []):
                        currency = detail.get('ccy', '')
                        balances[currency] = {
                            'free': float(detail.get('availBal', 0)),
                            'used': float(detail.get('frozenBal', 0)),
                            'total': float(detail.get('bal', 0))
                        }
            
            return balances
        except Exception as e:
            self.logger.error(f"获取现货余额失败: {e}")
            return {}
    
    def spot_place_order(self, symbol: str, order_type: str, side: str, 
                        amount: float, price: Optional[float] = None) -> Dict[str, Any]:
        """现货下单"""
        try:
            data = {
                'instId': symbol,
                'tdMode': 'cash',
                'side': side,
                'ordType': order_type,
                'sz': str(amount)
            }
            
            if order_type == 'limit' and price:
                data['px'] = str(price)
            
            result = self._make_request('POST', '/api/v5/trade/order', data=data)
            
            if result.get('data'):
                order_data = result['data'][0]
                return {
                    'id': order_data.get('ordId'),
                    'symbol': symbol,
                    'side': side,
                    'type': order_type,
                    'amount': amount,
                    'price': price,
                    'status': 'submitted'
                }
            
            return {}
        except Exception as e:
            self.logger.error(f"现货下单失败: {e}")
            return {}
    
    def get_futures_balance(self) -> Dict[str, Any]:
        """获取合约账户余额"""
        try:
            result = self._make_request('GET', '/api/v5/account/balance')
            balances = {}
            
            if result.get('data'):
                for account in result['data']:
                    for detail in account.get('details', []):
                        currency = detail.get('ccy', '')
                        balances[currency] = {
                            'free': float(detail.get('availBal', 0)),
                            'used': float(detail.get('frozenBal', 0)),
                            'total': float(detail.get('bal', 0))
                        }
            
            return balances
        except Exception as e:
            self.logger.error(f"获取合约余额失败: {e}")
            return {}
    
    def get_futures_position(self, symbol: Optional[str] = None) -> Dict[str, Any]:
        """获取合约仓位"""
        try:
            params = {}
            if symbol:
                params['instId'] = symbol
            
            result = self._make_request('GET', '/api/v5/account/positions', params=params)
            positions = {}
            
            if result.get('data'):
                for pos in result['data']:
                    inst_id = pos.get('instId', '')
                    positions[inst_id] = {
                        'symbol': inst_id,
                        'size': float(pos.get('pos', 0)),
                        'side': pos.get('posSide', ''),
                        'unrealized_pnl': float(pos.get('upl', 0)),
                        'entry_price': float(pos.get('avgPx', 0))
                    }
            
            return positions
        except Exception as e:
            self.logger.error(f"获取合约仓位失败: {e}")
            return {}
    
    def futures_place_order(self, symbol: str, order_type: str, side: str, 
                           amount: float, leverage: int, 
                           price: Optional[float] = None) -> Dict[str, Any]:
        """合约下单"""
        try:
            data = {
                'instId': symbol,
                'tdMode': 'cross',  # 全仓模式
                'side': side,
                'ordType': order_type,
                'sz': str(amount)
            }
            
            if order_type == 'limit' and price:
                data['px'] = str(price)
            
            result = self._make_request('POST', '/api/v5/trade/order', data=data)
            
            if result.get('data'):
                order_data = result['data'][0]
                return {
                    'id': order_data.get('ordId'),
                    'symbol': symbol,
                    'side': side,
                    'type': order_type,
                    'amount': amount,
                    'price': price,
                    'leverage': leverage,
                    'status': 'submitted'
                }
            
            return {}
        except Exception as e:
            self.logger.error(f"合约下单失败: {e}")
            return {}
    
    def get_kline(self, symbol: str, period: str, limit: int = 100) -> List[Dict[str, Any]]:
        """获取K线数据"""
        try:
            params = {
                'instId': symbol,
                'bar': period,
                'limit': str(limit)
            }
            
            result = self._make_request('GET', '/api/v5/market/candles', params=params)
            klines = []
            
            if result.get('data'):
                for kline in result['data']:
                    klines.append({
                        'timestamp': int(kline[0]),
                        'open': float(kline[1]),
                        'high': float(kline[2]),
                        'low': float(kline[3]),
                        'close': float(kline[4]),
                        'volume': float(kline[5])
                    })
            
            return klines
        except Exception as e:
            self.logger.error(f"获取K线数据失败: {e}")
            return []
    
    def get_latest_price(self, symbol: str) -> float:
        """获取最新价格"""
        try:
            params = {'instId': symbol}
            result = self._make_request('GET', '/api/v5/market/ticker', params=params)
            
            if result.get('data') and len(result['data']) > 0:
                return float(result['data'][0].get('last', 0))
            
            return 0.0
        except Exception as e:
            self.logger.error(f"获取最新价格失败: {e}")
            return 0.0
    
    def cancel_order(self, order_id: str, symbol: str) -> Dict[str, Any]:
        """取消订单"""
        try:
            data = {
                'instId': symbol,
                'ordId': order_id
            }
            
            result = self._make_request('POST', '/api/v5/trade/cancel-order', data=data)
            
            if result.get('data'):
                return {
                    'id': order_id,
                    'status': 'cancelled'
                }
            
            return {}
        except Exception as e:
            self.logger.error(f"取消订单失败: {e}")
            return {}
    
    def get_order_status(self, order_id: str, symbol: str) -> Dict[str, Any]:
        """获取订单状态"""
        try:
            params = {
                'instId': symbol,
                'ordId': order_id
            }
            
            result = self._make_request('GET', '/api/v5/trade/order', params=params)
            
            if result.get('data') and len(result['data']) > 0:
                order = result['data'][0]
                return {
                    'id': order.get('ordId'),
                    'symbol': symbol,
                    'status': order.get('state', ''),
                    'filled': float(order.get('fillSz', 0)),
                    'remaining': float(order.get('sz', 0)) - float(order.get('fillSz', 0)),
                    'price': float(order.get('px', 0)),
                    'average': float(order.get('avgPx', 0)) if order.get('avgPx') else None
                }
            
            return {}
        except Exception as e:
            self.logger.error(f"获取订单状态失败: {e}")
            return {}